package sim;

import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;

public class Router {
	static int attacker;
	static int victim;
	static int numOfRouters;
	static LSA[] memory;
	static boolean[] isReceiving;
	static Hashtable<Integer, LinkedList<Adjacency>> adjMatrix;
	static boolean generatedFighback = false;
	
	public static void init(int attacker_, int victim_, int numOfRouters_, Hashtable<Integer, LinkedList<Adjacency>> adjMatrix_){
		attacker = attacker_;
		victim = victim_;
		numOfRouters = numOfRouters_;
		memory = new LSA[numOfRouters];
		isReceiving = new boolean[numOfRouters];
		for (int i = 0 ; i < numOfRouters ; i++){
			memory[i] = new LSA(-1,LSAType.NoRoute);
			isReceiving[i] = true;
			if (i == victim){
				memory[i] = new LSA(1, LSAType.Fightback);
			}
			if (i == attacker){
				memory[i] = new LSA(1, LSAType.FalseLSA);
			}
		}
		adjMatrix = adjMatrix_;
		generatedFighback = false;
	}
	
	public static void send(int routerID, LSA payload){
		LinkedList<Adjacency> adjList = adjMatrix.get(routerID);
		for (Adjacency adj: adjList){
			Experiment.eventHeap.add(new ReceiveEvent(Common.time + Common.SEND_TIME, adj.dst, payload));
		}
	}
	
	public static void receive(int routerID, LSA payload){
//		if (routerID == victim){
//			if (payload.type == LSAType.FlagUp){
//				acceptPackets = true;
//				return;
//			}
//			if (memory[routerID].ID > payload.ID){
//				if (!generatedFighback){
//					Experiment.eventHeap.add(new SendEvent(Common.time + Common.FIGHTBACK_DELAY, routerID, memory[routerID]));
//					Experiment.eventHeap.add(new ReceiveEvent(Common.time + Common.FIGHTBACK_DELAY, routerID, new LSA(-2, LSAType.FlagUp)));
//					generatedFighback = true;
//					acceptPackets = false;
//				}
//				else if (!acceptPackets){
//					return;
//				}
//				else {
//					Experiment.eventHeap.add(new SendEvent(Common.time + Common.PROCESS_TIME, routerID, memory[routerID]));
//				}				
//			}
//		}
		if (payload.type == LSAType.FlagUp){
			isReceiving[routerID] = true;
			return;
		}
		if (isReceiving[routerID]){
			if (routerID == victim && memory[routerID].ID > payload.ID){
				if (!generatedFighback){
					// send fightback twice (including retransmission)
					Experiment.eventHeap.add(new SendEvent(Common.time + Common.FIGHTBACK_DELAY, routerID, memory[routerID]));
					Experiment.eventHeap.add(new SendEvent(Common.time + Common.FIGHTBACK_DELAY + Common.RETRANSMISSION, 
												routerID, memory[routerID]));
					// do not receive packets for MinLS 
					isReceiving[routerID] = false;
					Experiment.eventHeap.add(new ReceiveEvent(Common.time + Common.MIN_LS, routerID, new LSA(-2, LSAType.FlagUp)));
					generatedFighback = true;
				}
				else{// generate fightback only once
					return;
	//				Experiment.eventHeap.add(new SendEvent(Common.time + Common.PROCESS_TIME, routerID, memory[routerID]));
	//				Experiment.eventHeap.add(new SendEvent(Common.time + Common.PROCESS_TIME + Common.RETRANSMISSION, 
	//											routerID, memory[routerID]));
				}
			}
			else if (memory[routerID] == null || memory[routerID].ID < payload.ID){
				memory[routerID] = payload;
				Experiment.eventHeap.add(new SendEvent(Common.time + Common.PROCESS_TIME, routerID, payload));
				Experiment.eventHeap.add(new SendEvent(Common.time + Common.PROCESS_TIME + Common.RETRANSMISSION, 
											routerID, payload));
				// do not receive packets for MinLS 
				isReceiving[routerID] = false;
				Experiment.eventHeap.add(new ReceiveEvent(Common.time + Common.MIN_LS, routerID, new LSA(-2, LSAType.FlagUp)));
			}
		}
	}
	
	public static int distance(int src, int target){
		HashSet<Integer> open = new HashSet<Integer>();
		HashSet<Integer> closed = new HashSet<Integer>();
		int retVal = 1;
		open.add(src);
		while (!open.isEmpty()){
			HashSet<Integer> nextOpen = new HashSet<Integer>();
			for (int router:open){
				if (!closed.contains(router)){
					LinkedList<Adjacency> neighbors = adjMatrix.get(router);
					for (Adjacency neighbor:neighbors){
						if (neighbor.dst == target) return retVal;
						nextOpen.add(neighbor.dst);
					}
					closed.add(router);
				}
			}
			open = nextOpen;
			retVal++;
		}
		return -1;
	}
	
}
